#include <SDL/SDL.h>
#include <iostream>
#include <pthread.h>

using namespace std;

void callback(void *userdata, Uint8 *stream, int len);
SDL_AudioSpec desired, obtained;
SDL_AudioSpec wavSpec;
Uint32 wavLen;
Uint8 *wavBuffer, *wavNewBuf;
SDL_AudioCVT cvt;

typedef struct Sound {
  int position;
  Uint8 *buffer;
  int size;
} Sound;

void  * rodarSom(void * argument){
  Sound *local = (Sound *) argument;
  SDL_PauseAudio(0);
  
  while (true){
    SDL_LockAudio();
    if ((*local).position >= (*local).size)
      break;
    SDL_UnlockAudio();
    
    SDL_Delay(10);
  }
  SDL_PauseAudio(1);		
  
  SDL_Delay(100);
  
  free(wavNewBuf);
  SDL_CloseAudio();
  pthread_exit(NULL);
}

class Audio{
public:
  Sound sound;
  SDL_AudioSpec desired, obtained;
  void rodar(const char *caminho);
  Audio();
};

void Audio::rodar(const char *caminho){
  
  sound.position = -1;
  sound.size = 0;
  sound.buffer = 0;
  
  
  desired.freq = 44100;
  desired.format = AUDIO_S16;
  desired.channels = 2;
  desired.samples = 4096;
  desired.callback = callback;
  desired.userdata = (void *) &sound;
  
  int rc = SDL_OpenAudio(&desired, &obtained);
  
  if (rc != 0) {
    cerr << "Erro na abertura do dispositivo de audio: " <<
    SDL_GetError() << endl;
  }  
  
  if (SDL_LoadWAV(caminho, &wavSpec, &wavBuffer, &wavLen) == NULL) {
    cout << "Falha! " << SDL_GetError() << endl;
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
  }
  
  rc = SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq,
			 obtained.format, obtained.channels, obtained.freq);
  
  if (rc != 0) {
    cout << "Falha! " << SDL_GetError() << endl;
    SDL_FreeWAV(wavBuffer);
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
  }
  
  cvt.len = wavLen;
  wavNewBuf = (Uint8 *) malloc(cvt.len * cvt.len_mult);
  
  if (wavNewBuf == NULL) {
    cerr << "Sem memoria para um novo buffer!" << endl;
    SDL_FreeWAV(wavBuffer);
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
  }
  
  memcpy(wavNewBuf, wavBuffer, wavLen);
  cvt.buf = wavNewBuf;
  
  rc = SDL_ConvertAudio(&cvt);
  
  if (rc != 0) {
    cerr << "Erro na conversao do audio!" << endl;
    SDL_FreeWAV(wavBuffer);
    free(wavNewBuf);
    SDL_QuitSubSystem (SDL_INIT_AUDIO);
  }
  
  SDL_FreeWAV(wavBuffer);
  
  
  SDL_LockAudio();
  sound.position = 0;
  sound.buffer = wavNewBuf;
  sound.size = cvt.len * cvt.len_mult;
  SDL_UnlockAudio();
  pthread_t t1;
  pthread_create( &t1, NULL, rodarSom, (void *)(&sound));

}

void 
callback(void *userdata, Uint8 *audio, int length)
{    
  memset(audio, 0, length);
  
  Sound *sound = (Sound *) userdata;
  
  if (sound->position == -1 || sound->position >= sound->size)
    return;
  
  int nextSamplesLength;
  
  if (sound->position + length > sound->size)
    nextSamplesLength =  sound->size - sound->position;
  else
    nextSamplesLength = length;
  
  SDL_MixAudio(audio, sound->buffer + sound->position, nextSamplesLength, 
	       SDL_MIX_MAXVOLUME / 2);
  
  sound->position += nextSamplesLength;
}

Audio::Audio(){}